Skip to content

Strict description reader, phases 4-5 and warnings (Stage 1, PR 3 of 3) - #185

Merged
guijacquemet merged 5 commits into
mainfrom
claude/description-reader-phases-4-5
Nov 7, 2026
Merged

guijacquemet merged 5 commits into
mainfrom
claude/description-reader-phases-4-5

Conversation

@guijacquemet

@guijacquemet guijacquemet commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

PR 3 of the Stage 1 design (section 5): identity (phase 4), semantics (phase 5) and the three warnings of Spec experiment-v1 at Spec commit 8922a359ec261148a23a4b6e740a1bdb69e9e318, so every Spec valid/ fixture passes with its exact warnings and every invalid/ fixture fails with its exact code. Additive: pure new modules plus tests; no run, notebook, CLI command or output uses them yet. Waits for R7 / Gate G0 like all of Stage 1.

Stacked on PR 2 (#184), which is stacked on PR 1 (#183). This branch contains both commits, so until they merge the diff shows all three; the base is main.

What changed

  • description_identity.py (phase 4): the Label rules by calling the brick's check_identity_text (not a copy; a test compares its refused names with the text), the Token regular expression and the Label rules as two separate codes, the reserved list (channel IDs, mask IDs; mask IDs also the in part), NFC (refused as written, never normalized), single, and the case-collision scopes (condition IDs and source folders, repeat IDs, source repeats, technical repeat IDs and folders, channels, masks, mask source types, used_ids per kind).
  • description_semantics.py (phase 5): exact reference resolution (no case folding, NFC or display name), unit channels (declared, no repeats), the RGB rules, header-name duplicates (after NFC), used_ids (duplicates, every declared ID listed), display-name uniqueness per kind, one folder one ID, source repeats and E_REPEAT_ID_SHADOWS_SOURCE, mask source types, the reference graph (self, unknown, cycle).
  • description_warnings.py: W_UNUSED_CHANNEL, W_REPEAT_MISSING_CONDITIONS, W_REFERENCE_UNPAIRED.
  • description_walk.py (members and units); description_rules.py gains TOKEN, RESERVED, RGB_TOKENS, OBJECT_SET_PART.
  • description_read.read_description_bytes(data: bytes): phases 1-5, then the warnings (only for a file without errors). read_structure (phases 1-3) stays.
  • Layers declared (all pure); AGENTS.md and CHANGELOG.md updated.

Tests (new)

  • tests/test_spec_reader.py: all 36 valid files accepted with exactly their warnings; all 130 invalid files refused with exactly their code set from exactly their phase and no warning; each error has a path and a value; 12 transitions valid alone with the warnings their catalogue rows state; non-NFC identities reported as written; IDs kept as written (v26); a display name never stands for an ID; one code per rule.
  • tests/test_spec_rules_table.py (extended): the reserved list, the Token pattern, the RGB tokens and the Label rule's refused names equal the vendored text.
  • tests/test_spec_refval_crosscheck.py (the oracle): the vendored reference validator and the reader give the same phase, code set, table paths (where the oracle's are precise) and warnings on all 178 fixtures and on 57,279 one-step mutations of 16 valid fixtures (delete, retype, rename, add a field, duplicate, or a value or key borrowed from the same file). The corpus reaches every phase 4 and 5 code (a test asserts it), so no rule goes uncompared. 0 differences.

Regression check: on PR 2's tip read_description_bytes does not exist, so these tests fail at import there. Mutation check: with the NFC and outer-whitespace checks disabled, 34 of these tests fail (restored afterwards).

Points where the text or the oracle is ambiguous or differs (for review)

  1. used_ids entries and the reserved list. The text says the entries follow "the identity rules of their kind"; a retired channel ID on the reserved list is neither required nor excluded, and no fixture decides it. Followed the oracle (its AMBIGUITY note): Label + Token + NFC, not the reserved list.
  2. Paths. The oracle's path for E_SOURCE_FOLDER_REUSED, E_SOURCE_FOLDER_CONFLICT, E_SOURCE_REPEAT_REUSED, E_MASK_SOURCE_TYPE_REUSED and E_REFERENCE_CYCLE is the table, a bare ID or a list; the reader names each offending field (for example conditions.DrugA.source_folder), one issue per field, with the shared value. The oracle abbreviates the table in the path of E_DISPLAY_NAME_COLLISION (c.CTRL...); the reader writes the full path. The cross-check compares codes only for the first five and normalizes the last. Section 6 asks for a table path, not which one.
  3. Display-name keys use the text's definition NFC(casefold(NFC(x))), as the oracle does.
  4. A single misuse that is not about spelling carries an explanatory string as its value (as the oracle does).
  5. The warnings of a transition fixture are not in its Expected header; the catalogue's case column states them (W_REPEAT_MISSING_CONDITIONS for t03, t06, t07, t08) and the test compares with that.
  6. Carried from PR 2: duplicate key inside an inline table is E_DUPLICATE_KEY per section 2.2 where the oracle's mapping says E_TOML_SYNTAX.

Tests run

python tools/check.py: ruff check clean, ruff format clean, code rules clean, 3521 passed, 4 skipped (PR 2: 3192 passed, 4 skipped; PR 1: 2713; main: 2316 passed, 4 skipped). The same 4 pre-existing skips; none in a Spec test. Not run: the Windows, macOS and Python 3.11 legs of CI (the new tests run only on Python 3.13 here). The machine's disk filled during two earlier runs (OSErrors in unrelated tests); the numbers above are from a clean full run.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm

Adds tests/spec_fixtures/: the MorphoBricks-Spec fixtures/ folder, docs/experiment-v1.md and
the reference validator (a test oracle only) at Spec commit 8922a359ec26, with PIN.toml.

- tests/test_spec_pin.py: the vendored fixtures/ folder must hash (git tree algorithm) to
  `git rev-parse <sha>:fixtures`; the two single files to their blob IDs. One changed, added,
  removed, renamed or CRLF-converted file fails.
- tests/spec_gate.py: a skipped or xfailed test in any tests/test_spec_*.py is a failure.
- tests/test_spec_no_fallback.py: no second table of codes or names, no lenient, legacy or
  default-version path in the reader modules (description*.py); the scan is itself tested.
- tests/test_spec_vendored_missing.py: with the vendored tree removed the suite fails.
- tests/test_spec_catalogue.py: every fixture has its header lines and a catalogue row.

Tests only: no change to the package, the CLI, the notebook or any output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
New pure modules for the experiment-description reader (Spec experiment-v1 sections 2-4, 6):

- description_rules: the one table of the rules (codes, field tables, forbidden names, the
  enumerations, the supported version set), compared with the vendored text by a test.
- description_report: DescriptionIssue (code, table path, value) and DescriptionReport.
- description_read: phase 1 (raw bytes: UTF-8 without BOM, TOML 1.0, a key declared twice in any
  spelling as E_DUPLICATE_KEY), phase 2 (schema and version, exact types) and the driver
  read_structure(bytes), which stops after the first failing phase.
- description_structure: phase 3 (closed field sets, forbidden fields and ID keys, required
  fields, exact types, non-empty tables and arrays, dates, ranges, enumerations, N35).

Every invalid/ fixture of phases 1-3 (65) gives exactly its Expected codes from its phase;
all other fixtures pass these phases. The vendored reference validator is the oracle: the
reader agrees with it on every fixture and on ~17,000 one-step mutations of nine valid ones.
Not used by any run, notebook or CLI command yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
Completes the pure experiment-description reader (Spec experiment-v1 sections 3-6):

- description_identity (phase 4): the Label rules by calling the brick's check_identity_text, the
  Token rule, the reserved list, NFC (refused as written, never normalized), `single`, and the
  case-collision scopes; one code per broken rule.
- description_semantics (phase 5): exact reference resolution, unit channels, the RGB rules,
  header-name and used_ids duplicates, display-name uniqueness per kind, one folder one ID,
  source repeats and shadowing, mask source types, the reference graph and cycles.
- description_warnings: W_UNUSED_CHANNEL, W_REPEAT_MISSING_CONDITIONS, W_REFERENCE_UNPAIRED.
- description_read.read_description_bytes(bytes) runs phases 1-5 then the warnings.

All 36 valid/ fixtures pass with their exact warnings, all 130 invalid/ fixtures fail with their
exact code set from their phase, and the 12 transitions are valid alone. The vendored reference
validator stays the oracle: the reader agrees with it on every fixture and on 57,279
one-step mutations of 16 valid fixtures, which reach every phase 4 and 5 code.
Not used by any run, notebook or CLI command yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
The pin test compared refval's __file__ with a POSIX suffix, which fails on Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
@guijacquemet
guijacquemet merged commit 93b3c4c into main Nov 7, 2026
7 checks passed
@guijacquemet
guijacquemet deleted the claude/description-reader-phases-4-5 branch November 7, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants